#include "StdAfx.h"
#include "VSqlItem.h"

// VSqlItem_maps //////////////////////////////////////////////////////////////

VSqlItem_maps::VMaps VSqlItem_maps::m_maps;

//
VSqlItem_maps::VMaps::VMaps()
{
	m_itemTypeToDisplayName[ESqlItemType_Unknown]		= "Unknown";
	m_itemTypeToDisplayName[ESqlItemType_folder]		= "folder";
	m_itemTypeToDisplayName[ESqlItemType_aliasNode]	= "aliasNode";
	m_itemTypeToDisplayName[ESqlItemType_table]			= "table";
	m_itemTypeToDisplayName[ESqlItemType_idRec]			= "idRec";
	m_itemTypeToDisplayName[ESqlItemType_sRef]			= "sRef";
	m_itemTypeToDisplayName[ESqlItemType_mRef]			= "mRef";
	m_itemTypeToDisplayName[ESqlItemType_string]		= "string";
	m_itemTypeToDisplayName[ESqlItemType_integer]		= "integer";
	m_itemTypeToDisplayName[ESqlItemType_double]		= "double";
	m_itemTypeToDisplayName[ESqlItemType_date]			= "date";
	m_itemTypeToDisplayName[ESqlItemType_list]			= "list";
	m_itemTypeToDisplayName[ESqlItemType_itemList]	= "itemList";

/**********
	VMapItemTypeToDb::const_iterator it;
	for (it = m_itemTypeToDb.constBegin(); it != m_itemTypeToDb.constEnd(); ++it)
		m_dbToItemType[it.value()] = it.key();
***********/		
}

//
QString VSqlItem_maps::itemTypeToDisplayName(ESqlItemType v)
{
	Q_ASSERT(m_maps.m_itemTypeToDisplayName.contains(v));
	return m_maps.m_itemTypeToDisplayName[v];
}

// VSqlItem ///////////////////////////////////////////////////////////////////

//
VSqlItem &VSqlItem::setFolder
(
	const QString &keyName,
	const QString &displayName
)
{
	m_itemType.setValue(ESqlItemType_folder);
	m_keyName.setValue(keyName);
	m_displayName.setValue(displayName);

	return *this;
}

//
VSqlItem &VSqlItem::setTable
(
	const QString &keyName,
	const QString &displayName,
	const QString &nameInDb
)
{
	m_itemType.setValue(ESqlItemType_table);
	m_keyName.setValue(keyName);
	m_displayName.setValue(displayName);
	m_nameInDb.setValue(nameInDb);
	
	return *this;
}

//
VSqlItem &VSqlItem::setIdRec
(
	const QString &keyName
)
{
	m_itemType.setValue(ESqlItemType_idRec);
	m_keyName.setValue(keyName);
	m_displayName.setValue("idRec");
	m_nameInDb.setValue("idRec");

	return *this;
}

//
VSqlItem &VSqlItem::setString
(
	const QString &keyName,
	const QString &displayName,
	int length,
	const QString &nameInDb
)
{
	m_itemType.setValue(ESqlItemType_string);
	m_keyName.setValue(keyName);
	m_displayName.setValue(displayName);
	m_nameInDb.setValue(nameInDb);
	m_lenVal.setValue(length);

	return *this;
}

//
VSqlItem &VSqlItem::setMRef
(
	const QString &keyName,
	const QString &displayName,
	const QString &refTableKeyName,
	const QString &nameInDb
)
{
	m_itemType.setValue(ESqlItemType_mRef);
	m_keyName.setValue(keyName);
	m_displayName.setValue(displayName);
	m_nameInDb.setValue(nameInDb);
	
	setKeyNameRefIdItem(refTableKeyName);

	return *this;
}

//
VSqlItem &VSqlItem::setList
(
	const QString &keyName,
	const QString &displayName
)
{
	m_itemType.setValue(ESqlItemType_list);
	m_keyName.setValue(keyName);
	m_displayName.setValue(displayName);

	return *this;
}

//
VSqlItem &VSqlItem::setItemList
(
	const QString &keyName,
	const QVariant &tableRefIdItem
)
{
	m_itemType.setValue(ESqlItemType_itemList);
	m_keyName.setValue(keyName);
	m_refIdItem.setValue(tableRefIdItem);

	return *this;
}

//
void VSqlItem::setRecord(VSqlSharedRecordPtr &r) const
{
	r->clear();
	foreach (const QSqlField field,fieldList())
		r->append(field.value());
}

//
bool VSqlItem::isField() const 
{ 
	ESqlItemType t = eItemType(); 
	return 
		t == ESqlItemType_idRec		||
		t == ESqlItemType_sRef		||
		t == ESqlItemType_mRef		||
		t == ESqlItemType_string	||
		t == ESqlItemType_integer	||
		t == ESqlItemType_double	||
		t == ESqlItemType_date;
}

//
QString VSqlItem::dataTableName() const
{
	if (eItemType() == ESqlItemType_table)
	{
		QString name = m_nameInDb.value().toString();
			name.remove(QChar(' '));
		return name;
	}
	return QString();
}

//
QString VSqlItem::dataFieldNameAndType() const
{
	QString name = m_nameInDb.value().toString();
		name.remove(QChar(' '));
	if (name.isEmpty())
		return QString();

	switch (eItemType())
	{
		case ESqlItemType_string:
		{
			int len;
			if (m_lenVal.isNull())
				len = 100;
			else
				len = m_lenVal.value().toUInt();
	
			if (len == 0)
				len = 100;

			return name + QString(" varchar(%1)").arg(len); 
		}
		return true;

		case ESqlItemType_idRec:		return name + " bigint not null"; return true;
		case ESqlItemType_sRef:			return name + " bigint"; return true;				
		//case ESqlItemType_mRef:			v = name + ""; return true;
		case ESqlItemType_integer:	return name + " integer"; return true;
		case ESqlItemType_double:		return name + " float"; return true;
		case ESqlItemType_date:			return name + " date"; return true;
	}

	return QString();
}

// VSqlItem_insert ////////////////////////////////////////////////////////////

//
VSqlItem_insert::VSqlItem_insert(const QSqlDatabase &db)
	:m_query(db)
	,m_genId(db)
{
	VSqlItem sqlItem;
	VSqlFieldList fields = sqlItem.fieldList();

	QSqlField status = sqlItem.status();
		fields << status;

	m_query.prepare(sqlItem.tableName(),fields);
}

//
void VSqlItem_insert::exec(VSqlItem &sqlItem)
{
	QVariant idItem	= m_genId.exec();
	sqlItem.setIdItem(idItem);
	quint64 vIdItem = idItem.toULongLong();

	if (
		!sqlItem.isFolder() && 
		!sqlItem.isList() &&
		!sqlItem.isItemList()
	)
	{
		QString nameInDb = sqlItem.nameInDb().value().toString();
			nameInDb.remove(QChar(' '));
		if (nameInDb.isEmpty())
			sqlItem.setNameInDb(QString("object_%1").arg(vIdItem));
	}

	QString keyName = sqlItem.keyName().value().toString();
		keyName.remove(QChar(' '));
	if (keyName.isEmpty())
		sqlItem.setKeyName(QString("object_%1").arg(vIdItem));

	QString displayName = sqlItem.displayName().value().toString();
		displayName.remove(QChar(' '));
	if (displayName.isEmpty())
		sqlItem.setDisplayName(QString("object_%1").arg(vIdItem));

	VSqlFieldPtrList fields = sqlItem.fieldPtrList();

	QSqlField keyNameUpper = sqlItem.keyNameUpper();
		sqlItem.setKeyNameUpper(sqlItem.keyName().value().toString().toUpper());

	QSqlField status = sqlItem.status();
		status.setValue(ESqlDbStatus_actual);
		fields << &status;

	m_query.exec(sqlItem.idItem(),fields);
}

// VSqlItem_select_byAnd ///////////////////////////////////////////////////

//
void VSqlItem_select_byAnd::prepare
(
	const VSqlFieldList &fieldsSelect,
	const VSqlFieldList &fieldsAnd
)
{
	VSqlItem sqlItem;
	QString sqlWhere = QString(sqlItem.status().name() + " = %1").arg(ESqlDbStatus_actual);

	foreach (QSqlField fieldAnd,fieldsAnd)
	{
		if (fieldAnd.name() == sqlItem.keyName().name())
			fieldAnd = sqlItem.keyNameUpper();

		sqlWhere += " and " + fieldAnd.name() + " = :" + fieldAnd.name();
	}

	m_query.prepare(sqlItem.tableName(),fieldsSelect,sqlWhere);
}

//
void VSqlItem_select_byAnd::exec(const VSqlFieldList &fieldsAnd)
{
	VSqlItem sqlItem;
	VSqlFieldList newFieldsAnd;

	foreach (const QSqlField fieldAnd,fieldsAnd)
	{
		if (fieldAnd.name() == sqlItem.keyName().name())
		{
			QSqlField keyNameUpper = sqlItem.keyNameUpper();
			keyNameUpper.setValue(fieldAnd.value().toString().toUpper());
			newFieldsAnd << keyNameUpper;
		}
		else
			newFieldsAnd << fieldAnd;
	}

	m_query.exec(newFieldsAnd);
}

// VSqlItemMap ////////////////////////////////////////////////////////////////

//
VSqlItemMap::VSqlItemMap(const QSqlDatabase &db)
	:m_db(db)
{
}

//
QList<VSqlSharedRecordPtr> VSqlItemMap::recordPtrList_byIdItemList(const QVariantList &idItemList)
{
	QList<VSqlSharedRecordPtr> outList;
	VSqlItem item;
	VSqlFieldList fieldList = item.fieldList();
	VSqlFieldPtrList fieldPtrList = item.fieldPtrList();
	VSqlItem_select_byAnd selItem(m_db);
	bool isPrepare = false;

	foreach (const QVariant v,idItemList)
	{
		quint64 idItem = v.toULongLong();

		if (m_map_byIdItem.contains(idItem))
		{
			outList << m_map_byIdItem[idItem];
			continue;
		}

		if (!isPrepare)
		{
			selItem.prepare(fieldList,VSqlFieldList() << item.idItem());
			isPrepare = true;
			
			if (VSqlLog::canWrite())
				VSqlLog::write("VSqlItemMap::recordPtrList_byIdItemList",selItem.sql());
		}

		item.setIdItem(idItem);
		selItem.exec(VSqlFieldList() << item.idItem());

		if (!selItem.next(fieldPtrList))
			continue;

		VSqlSharedRecordPtr newRecord(new QVariantList);
		VSqlQuery_fieldsPtrToRecord(fieldPtrList,newRecord);
		m_map_byIdItem[idItem] = newRecord;
		outList << newRecord;
	}

	return outList;
}

//
VSqlSharedRecordPtr VSqlItemMap::recordPtr_byIdItem(const QVariant &idItem)
{
	QList<VSqlSharedRecordPtr> list = recordPtrList_byIdItemList(QVariantList() << idItem);
	if (list.count())
		return list[0];
		
	return VSqlSharedRecordPtr();
}

//
QList<VSqlItem> VSqlItemMap::itemList_byIdItemList(const QVariantList &idItemList)
{
	QList<VSqlItem> outList;

	foreach (const VSqlSharedRecordPtr record,recordPtrList_byIdItemList(idItemList))
	{
		VSqlItem item;
			item.setValues(record);
		outList << item;
	}

	return outList;
}

//
VSqlItem VSqlItemMap::item_byIdItem(const QVariant &idItem)
{
	VSqlItem item;
	VSqlSharedRecordPtr record = recordPtr_byIdItem(idItem);
	if (record)
		item.setValues(record);

	return item;
}

//
QList<VSqlSharedRecordPtr> VSqlItemMap::recordPtrList_byKeyNameList(const QVariantList &keyNameList)
{
	QList<VSqlSharedRecordPtr> outList;
	VSqlItem item;
	VSqlItem_select_byAnd selItem(m_db);
	bool isPrepare = false;

	foreach (const QVariant v,keyNameList)
	{
		QString keyNameUpper = v.toString().toUpper();

		if (m_map_byKeyName.contains(keyNameUpper))
		{
			outList << m_map_byKeyName[keyNameUpper];
			continue;
		}

		if (!isPrepare)
		{
			selItem.prepare(
				VSqlFieldList() << item.idItem(),
				VSqlFieldList() << item.keyNameUpper()
			);
			isPrepare = true;

			if (VSqlLog::canWrite())
				VSqlLog::write("VSqlItemMap::recordPtrList_byKeyNameList",selItem.sql());
		}


		item.setKeyNameUpper(keyNameUpper);
		selItem.exec(VSqlFieldList() << item.keyNameUpper());

		if (!selItem.next(VSqlFieldPtrList() << item.idItemPtr()))
			continue;

		VSqlSharedRecordPtr record = recordPtr_byIdItem(item.idItem().value());
		Q_ASSERT(record != NULL);
		m_map_byKeyName[keyNameUpper] = record;
		
		outList << record;
	}

	return outList;
}

//
VSqlSharedRecordPtr VSqlItemMap::recordPtr_byKeyName(const QVariant &keyName)
{
	QList<VSqlSharedRecordPtr> list = recordPtrList_byKeyNameList(QVariantList() << keyName);
	if (list.count())
		return list[0];

	return VSqlSharedRecordPtr();
}

//
VSqlItem VSqlItemMap::item_byKeyName(const QVariant &keyName)
{
	VSqlItem item;
	VSqlSharedRecordPtr record = recordPtr_byKeyName(keyName);
	if (record)
		item.setValues(record);

	return item;
}

//
void VSqlItemMap::insert(const QList<VSqlItem*> &itemListPtr)
{
	VSqlItem_insert insItem(m_db);

	if (VSqlLog::canWrite())
		VSqlLog::write("VSqlItemMap::insert",insItem.sql());

	foreach (VSqlItem *itemPtr,itemListPtr)
	{
		insItem.exec(*itemPtr);

		VSqlSharedRecordPtr newRecord(new QVariantList);
			itemPtr->setRecord(newRecord);

		quint64 vIdItem = itemPtr->idItem().value().toULongLong();
		QString vKeyNameUpper = itemPtr->keyNameUpper().value().toString();
		Q_ASSERT(m_map_byKeyName.contains(vKeyNameUpper) == false);

		m_map_byIdItem[vIdItem] = newRecord;
		m_map_byKeyName[vKeyNameUpper] = newRecord;
	}
}
